package cn.virens.lock;

import java.io.Serializable;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function;
import java.util.function.Supplier;

public class ReadWriteLock implements Serializable {
	private static final long serialVersionUID = -239682933489078070L;

	private final WriteInRead writeInRead = new WriteInRead(this);
	private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

	public <T> T read(Function<WriteInRead, T> function) {
		lock.readLock().lock();

		try {
			return function.apply(writeInRead);
		} finally {
			lock.readLock().unlock();
		}
	}

	public Object read0(FunctionWithout<WriteInRead> function) {
		return read(function);
	}

	public <T> T write(Supplier<T> supplier) {
		lock.writeLock().lock();

		try {
			return supplier.get();
		} finally {
			lock.writeLock().unlock();
		}
	}

	public Object write0(SupplierWithout supplier) {
		return write(supplier);
	}

	public static class WriteInRead implements Serializable {
		private static final long serialVersionUID = -6717124867893255665L;

		private final ReadWriteLock instance;

		private WriteInRead(ReadWriteLock instance) {
			this.instance = instance;
		}

		public <T> T write(Supplier<T> supplier) {
			instance.lock.readLock().unlock();
			instance.lock.writeLock().lock();

			try {
				return supplier.get();
			} finally {
				instance.lock.writeLock().unlock();
				instance.lock.readLock().lock();
			}
		}

		public Object write0(SupplierWithout supplier) {
			return write(supplier);
		}
	}

	public interface SupplierWithout extends Supplier<Object> {
		public void get0();

		@Override
		default Object get() {
			get0();

			return null;
		}
	}

	public interface FunctionWithout<T> extends Function<T, Object> {
		public void apply0(T t);

		@Override
		default Object apply(T t) {
			apply0(t);

			return null;
		}
	}
}
